/*
 * @(#)NetworkSession.java 1.0 2009/03/28
 */
package stardust.core.network;


import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;


/**
 * <code>TCPNetworkSession</code> 类实现维护 TCP 类型网络会话的周期。
 * <p/>
 * @see NetworkState
 * @see UDPNetworkState
 * @version 1.0, 2009/03/28
 * @author ChenChang
 */
public class TCPNetworkSession extends NetworkSession
{

  private ServerSocket listener;

  private Socket connector;

  public TCPNetworkSession()
  {
    super(ProtocolType.tcp);
  }

  public TCPNetworkSession(Socket socket)
  {
    this();
    this.connector = socket;
  }

  public NetworkState getState()
  {
    if ((this.listener != null && this.listener.isBound())
        || (this.connector != null && (!this.connector.isClosed() && this.connector.isConnected() && !this.connector.isInputShutdown() && !this.connector.isOutputShutdown())))
    {
      super.state = NetworkState.open;
    }
    else
    {
      super.state = NetworkState.closed;
    }

    return super.state;
  }

  public void listen(String address, int port) throws NetworkException
  {
    this.disconnect();

    try
    {
      this.listener = new ServerSocket(port, 255, InetAddress.getByName(address));
    }
    catch(UnknownHostException e)
    {
      this.disconnect();
      throw new NetworkException("无法在 '" + (address == null ? "*" : address) + ":" + port + "' 侦听连接，找不到主机地址。", e);
    }
    catch(IOException e)
    {
      this.disconnect();
      throw new NetworkException("无法在 '" + (address == null ? "*" : address) + ":" + port + "' 侦听连接，I/O 状态异常。", e);
    }
  }

  public NetworkSession accept(int timeout) throws NetworkException
  {
    if (this.getState() != NetworkState.open)
    {
      throw new NetworkException("侦听器处于关闭状态。");
    }
    else
    {
      try
      {
        this.listener.setSoTimeout(timeout < 0 ? 0 : timeout);

        return new TCPNetworkSession(this.listener.accept());
      }
      catch(SocketException e)
      {
        throw new NetworkException("无法接受连接请求，Socket 状态异常。", e);
      }
      catch(IOException e)
      {
        throw new NetworkException("无法接受连接请求，Socket I/O 状态异常。", e);
      }
    }
  }

  public void connect(String address, int port) throws NetworkException
  {
    this.disconnect();

    try
    {
      this.connector = new Socket(address, port);
    }
    catch(UnknownHostException e)
    {
      this.disconnect();
      throw new NetworkException("无法与 '" + address + ":" + port + "' 建立连接，找不到主机地址。", e);
    }
    catch(IOException e)
    {
      this.disconnect();
      throw new NetworkException("无法与 '" + address + ":" + port + "' 建立连接，该端口未开放或其它 I/O 状态异常。", e);
    }
  }

  public void disconnect()
  {
    if (this.connector != null)
    {
      try
      {
        this.connector.shutdownInput();
        this.connector.shutdownOutput();
        this.connector.close();
      }
      catch(IOException e)
      {
        ;
      }
      this.connector = null;
    }
    
    if (this.listener != null)
    {
      try
      {
        this.listener.close();
      }
      catch(IOException e)
      {
        ;
      }
      this.listener = null;
    }

    super.state = NetworkState.closed;
  }

  public void apply()
  {
    try
    {
      if (this.listener != null)
      {
        this.listener.setSoTimeout(this.getConfiguration().getTimeout());
      }

      if (this.connector != null)
      {
        this.connector.setSoTimeout(this.getConfiguration().getTimeout());
      }
    }
    catch(SocketException e)
    {
      throw new NetworkException("无法应用网络变更。", e);
    }
  }

  public int write(byte[] data, int offset, int len) throws NetworkException
  {
    try
    {
      int packetSize = super.getConfiguration().getPacketSize();

      if (len > packetSize)
      {
        this.connector.getOutputStream().write(data, offset, packetSize);
        this.connector.getOutputStream().flush();

        return packetSize;
      }
      else
      {
        this.connector.getOutputStream().write(data, offset, len);
        this.connector.getOutputStream().flush();

        return len - offset;
      }
    }
    catch(IOException e)
    {
      throw new NetworkException("写入数据时异常。", e);
    }
  }

  public int write(byte data) throws NetworkException
  {
    try
    {
      this.connector.getOutputStream().write(data);
      this.connector.getOutputStream().flush();

      return 1;
    }
    catch(IOException e)
    {
      throw new NetworkException("写入数据时异常。", e);
    }
  }

  public int read(byte[] data, int offset, int len) throws NetworkException
  {
    try
    {
      int r = this.connector.getInputStream().read(data, offset, len);

      // System.out.print(new String(data, offset, len));

      return r;
    }
    catch(IOException e)
    {
      throw new NetworkException("读取数据时异常。", e);
    }
  }

  public int read() throws NetworkException
  {
    try
    {
      return this.connector.getInputStream().read();
    }
    catch(IOException e)
    {
      throw new NetworkException("读取数据时异常。", e);
    }
  }

  public String getLocalHost()
  {
    if (this.listener != null)
    {
      return this.listener.getInetAddress().getHostAddress();
    }
    else if (this.connector != null)
    {
      return this.connector.getLocalAddress().getHostAddress();
    }

    return null;
  }

  public Integer getLocalPort()
  {
    if (this.listener != null)
    {
      return this.listener.getLocalPort();
    }
    else if (this.connector != null)
    {
      return this.connector.getLocalPort();
    }

    return null;
  }

  public String getRemoteHost()
  {
    if (this.connector != null)
    {
      return this.connector.getInetAddress().getHostAddress();
    }

    return null;
  }

  public Integer getRemotePort()
  {
    if (this.connector != null)
    {
      return this.connector.getPort();
    }

    return null;
  }

}
